define("apiSvc", [], function(require, exports, module) {
	var config = null;
	var data_id = GetRandomNum(0, 10000);// ws分包发送的包序号
	var msgPt = /^(pkg[0-9]+) ([0-9]+) ([0-9]+);/;
	var ready = false;
	var onReady = [];

	var acts = {};
	var callings = {};

	function GetRandomNum(Min, Max) {
		var Range = Max - Min;
		var Rand = Math.random();
		return (Min + Math.round(Rand * Range));
	}

	function isType(type) {
		return function(obj) {
			return {}.toString.call(obj) == "[object " + type + "]"
		}
	}

	var isObject = isType("Object")
	var isString = isType("String")
	var isFunction = isType("Function")
	var _cid = 0
	function cid() {
		return (_cid++).toString()
	}
	// 处理配置文件
	require.async('config', function(c) {
		config = c;
		var dbg = false;
		for ( var a in c) {
			var ctx = c[a]; // 一个服务器的上下文
			ctx.name = a;
			ctx.sid = GetRandomNum(10000, 100000); // TODO:sid应该第一次的时候存入本地存储中,以后读取
			dbg = dbg || ctx.debug;
			/**
			 * apiSvc.call('NvSvc','getList',param,function(ok,rslt,ext){});
			 * 
			 * apiSvc.call('NvSvc.getList',param,function(ok,rslt,ext){});
			 * 
			 * 成功调用返回null,否则返回错误信息.
			 */
			exports[a] = function(ctx) {
				var fun = function(svc, act, param, cb) {
					var argsLen = arguments.length;
					if (argsLen == 3) {
						cb = param;
						param = act;
						svc = svc.split('.');
						act = svc[svc.length - 1];
					} else if (argsLen == 4) {
						svc = svc.split('.');
						svc.push(act);
					} else {
						return 'apiSvc.' + a + '()参数错误';
					}
					if (svc.length < 2 || !isString(act) || !isObject(param) || !isFunction(cb)) {
						return 'apiSvc.' + a + '()参数错误';
					}

					for (var i = svc.length - 2; i >= 0; i--) {
						svc[i] = svc[i].charAt(0).toLowerCase() + svc[i].substr(1);
					}
					return call(ctx, svc, act, param, cb);
				};
				fun.context = ctx;
				return fun;
			}(ctx);
		}
		seajs.config({
			debug : dbg
		});

		ready = true;
		while (onReady.length > 0) {
			dbg = onReady.pop();
			if (isFunction(dbg)) {
				try {
					dbg();
				} catch (e) {
				}
			}
		}
	});
	window.apiSvc = exports;

	exports.onReady = function(fun) {
		if (ready) {
			if (isFunction(fun)) {
				fun();
			}
		} else {
			onReady.push(fun);
		}
	}

	// 发起请求.优先选择websocket,失败则用post请求.
	function request(ctx, act, svc, param, cb) {
		if (ctx.disableWs) {
			// 强制不用ws,用post请求
			post(ctx, act, svc, param, cb);
			return;
		}
		if (ctx.ws === undefined) {
			// 未初始化ws
			wsConnect(ctx, 0, function() {
				request(ctx, act, svc, param, cb);
			});
			return;
		}
		if (ctx.ws === null) {
			// 不支持ws,用post请求
			post(ctx, act, svc, param, cb);
		} else {
			// 支持ws
			wsSend(ctx, act, svc, param, cb);
		}
	}
	function updtDefSv(ctx, sv) {
		ctx.defaultServer = sv;
		var pt = {};
		pt[ctx.name] = ctx.apiSvcRoot.replace("{defaultServer}", sv);
		seajs.config({
			paths : pt
		});
	}

	// 多个服务器中找一个能用的
	function wsConnect(ctx, ind, cb) {
		// ws = null; // 测试ajax
		// cb(); // 如果ws不支持,尝试ajax
		// return;
		if (ind >= ctx.servers.length) {
			console.log('无服务器可用');
			return;
		}
		var url;
		if (ind <= 0 && ctx.defaultServer) {
			// 先处理 缺省地址
			ind = -1;
			url = ctx.defaultServer;
		} else {
			url = ctx.servers[ind];
		}
		try {
			var wsk = new WebSocket('ws://' + url + '/_ws');
			wsk.onopen = function(ev) {
				ctx.ws = wsk;
				ctx.svInd = ind;
				updtDefSv(ctx, url);
				cb(ctx);
			};
			wsk.onerror = function(ev) {
				console.log('error!');
				ctx.defaultServer = null;
				if (ind >= 0) {
					ctx.ws = undefined; // 允许断开后重连
				}
			};
			wsk.onclose = function(ev) {
				console.log('close: ' + ev.reason);
				if (ctx.ws === undefined) {
					wsConnect(ind + 1, cb);
				}
				ctx.ws = undefined; // 允许断开后重连
			};
			wsk.onmessage = function(ev) {
				wsProcRslt(ctx, ev.data);
			}
		} catch (e) {
			console.log(e);
			ctx.ws = null; // 如果不存在ws对象
			cb(ctx); // 如果ws不支持,尝试ajax
		}
	}
	// ws发送
	function wsSend(ctx, act, svc, param, cb) {
		var sn = cid(); // 序列号
		callings[sn] = [
				cb, sn
		];
		param = pkg(ctx, act, svc, sn, param); // 打包
		wsSend0(ctx, param);
	}
	// ws分包发送
	function wsSend0(ctx, x) {
		var len = x.length;
		if (len > 1024) {
			// 分包发送
			var cnt = Math.ceil(len / 1000), cnt2 = cnt - 1, l2 = len - cnt2 * 1000;
			var s = 'pkg' + (data_id++) + ' ' + cnt + ' ';
			for (var i = 0; i < cnt2; i++) { // 满包
				ctx.ws.send(s + i + ';' + x.substr(i * 1000, 1000));
			}
			if (l2 > 0) {
				ctx.ws.send(s + cnt2 + ';' + x.substr(cnt2 * 1000, l2));
			}
		} else {
			// 直接发送
			ctx.ws.send(x);
		}
	}
	// ws处理分包
	function wsProcRslt(ctx, msg) {
		if (!msg)
			msg = '';
		var m = msg.match(msgPt);
		if (!m) {
			procRslt(ctx, msg);
			return;
		}
		var total = m[2], index = m[3];
		if (index == 0) {
			ctx.msgTmp = msg.substr(m[0].length);
		} else {
			ctx.msgTmp += msg.substr(m[0].length);
		}
		if (index == total - 1) {
			procRslt(ctx, ctx.msgTmp);
			ctx.msgTmp = null;
		}
	}
	// /apiSvc/nvSvc/getlist?b=1&sid=sid&sn=1&s=sign
	function post(ctx, act, svc, param, cb) {
		var sn = cid(); // 序列号
		callings[sn] = [
				cb, sn
		];
		param = pkg(ctx, act, svc, sn, param); // 打包
		postSend(ctx, 0, '?b=0', param, cb, sn);
	}
	// 多个服务器中找一个能用的
	function postSend(ctx, ind, url0, data, cb, sn) {
		if (ind >= ctx.servers.length) {
			cb(false, null, {
				error : '服务器连接失败'
			});
			delete callings[sn];
			return;
		}
		var url;
		if (ind <= 0 && ctx.defaultServer) {
			// 先处理 缺省地址
			ind = -1;
			url = ctx.defaultServer;
		} else {
			url = ctx.servers[ind];
		}
		url += url0;
		url = 'http://' + url;
		// ajax请求
		// 先判断jQuery
		var ajax = jQuery && jQuery.ajax || $ && $.ajax;
		if (ajax && isFunction(ajax)) {
			ajax({
				type : "POST",
				url : url,
				contentType : 'text/plain',
				data : data,
				success : function(dt) {
					procRslt(ctx, dt);
				},
				processData : false,
				dataType : 'text',
				error : function(xhr, s, e) {
					console.log('url无效:' + url);
					ctx.defaultServer = null;
					postSend(ctx, ind + 1, url0, data, cb, sn);
				}
			});
		} else {
			// 再用原生ajax
			alert("未引入jQuery");
		}
	}

	// 签名,序列号,打包...
	// Nv,getList,sid,sn,sign;{}
	function pkg(ctx, act, svc, sn, param) {
		var data = JSON.stringify(param);
		return svc + ',' + act + ',' + ctx.sid + ',' + sn + ',' + sign(ctx, sn, data) + ';' + data;
	}
	// 签名
	function sign(ctx, sn, data) {
		return 'signRslt';
	}

	// sn,sign;{}
	function procRslt(ctx, rslt) {
		var ind = rslt.indexOf(';');
		if (ind < 3) {
			alert('返回值格式错误:' + rslt);
			return;
		}
		var sn = rslt.substr(0, ind).split(','); // sn,sign
		if (sn.length != 2) {
			alert('返回值格式错误:' + rslt);
			return;
		}
		var cb = callings[sn[0]];
		if (cb == null) {
			alert('返回值错误参数:' + rslt);
			return;
		}
		rslt = rslt.substr(ind + 1);
		var sig0 = sign(ctx, sn[0], rslt);
		if (sig0 != sn[1]) {
			cb[0](false, null, {
				clientErrMsg : '验签失败',
				data : rslt
			});
		} else {
			// 验签通过,调用回调
			try {
				ind = JSON.parse(rslt);
			} catch (e) {
				cb[0](false, null, {
					clientErrMsg : '返回值格式错误',
					e : e,
					data : rslt
				});
				delete callings[cb[1]];
				return;
			}
			cb[0](!!ind[0], ind[1], ind[2]);
		}
		delete callings[cb[1]];
	}

	/**
	 * 
	 */
	function call(ctx, svc, act, param, cb) {
		var an = ctx.name + '/' + svc.join('/');

		var fun = function(a, f) {
			if (!f) {
				acts[an] = a || null;
			}
			if (a == null) { // 没有js端验证脚本
				request(ctx, svc.pop(), svc.join('.'), param, cb);
				return;
			}
			var err = a.check(param); // 参数检查
			if (err) {
				cb(false, null, {
					clientErrMsg : err
				});
			} else {
				request(ctx, svc.pop(), svc.join('.'), param, cb);
			}
		};
		var act = acts[an];

		if (act !== undefined) {
			fun(act, true);
			return;
		}
		if (ctx.disableClientCheck) {
			fun(null);
			return;
		}

		require.async(an, fun);
	}
});
